package simpledb.execution;

import simpledb.common.DbException;
import simpledb.optimizer.LogicalPlan;
import simpledb.storage.Tuple;
import simpledb.storage.TupleDesc;
import simpledb.transaction.TransactionAbortedException;
import simpledb.transaction.TransactionId;

import java.io.Serializable;
import java.util.NoSuchElementException;

/**
 * Query 类用于封装查询的执行过程。
 * 它接收一个高层的 OpIterator 查询计划（由查询计划构造器创建），
 * 并在指定事务中运行该查询。
 *
 */
public class Query implements Serializable {

    private static final long     serialVersionUID = 1L;

    // 实际执行查询的物理操作迭代器
    transient private OpIterator  op;

    // 查询的逻辑计划（优化前的原始计划）
    transient private LogicalPlan logicalPlan;

    // 当前查询所属的事务 ID
    final TransactionId           tid;

    // 标记查询是否已经开始执行
    transient private boolean     started          = false;

    /**
     * 获取当前查询所属的事务 ID。
     *
     * @return 事务 ID
     */
    public TransactionId getTransactionId() {
        return this.tid;
    }

    /**
     * 设置查询的逻辑计划。
     *
     * @param lp 要设置的逻辑计划
     */
    public void setLogicalPlan(LogicalPlan lp) {
        this.logicalPlan = lp;
    }

    /**
     * 获取查询的逻辑计划。
     *
     * @return 查询的逻辑计划对象
     */
    public LogicalPlan getLogicalPlan() {
        return this.logicalPlan;
    }

    /**
     * 设置查询的物理执行计划（经过优化后的实际执行计划）。
     *
     * @param pp 物理执行计划迭代器
     */
    public void setPhysicalPlan(OpIterator pp) {
        this.op = pp;
    }

    /**
     * 获取查询的物理执行计划。
     *
     * @return OpIterator 类型的物理执行计划
     */
    public OpIterator getPhysicalPlan() {
        return this.op;
    }

    /**
     * 构造一个新的查询对象，并绑定到指定事务。
     *
     * @param t 事务 ID
     */
    public Query(TransactionId t) {
        tid = t;
    }

    /**
     * 构造一个新的查询对象，指定其物理执行计划和事务。
     *
     * @param root 查询的根节点（OpIterator 形式的物理计划）
     * @param t    所属事务 ID
     */
    public Query(OpIterator root, TransactionId t) {
        op = root;
        tid = t;
    }

    /**
     * 启动查询的执行。
     *
     * @throws DbException 如果数据库系统发生错误
     * @throws TransactionAbortedException 如果事务被中止（例如死锁）
     */
    public void start() throws DbException, TransactionAbortedException {
        op.open(); // 打开执行计划的根迭代器
        started = true; // 标记为已开始
    }

    /**
     * 获取查询结果元组的描述信息（字段名、类型等）。
     *
     * @return TupleDesc 对象
     */
    public TupleDesc getOutputTupleDesc() {
        return this.op.getTupleDesc();
    }

    /**
     * 判断是否还有更多元组可供读取。
     *
     * @return 如果有更多元组返回 true，否则返回 false
     * @throws DbException 如果数据库系统发生错误
     * @throws TransactionAbortedException 如果事务被中止
     */
    public boolean hasNext() throws DbException, TransactionAbortedException {
        return op.hasNext();
    }

    /**
     * 获取下一个查询结果元组。
     *
     * @return 下一个元组
     * @throws DbException 如果数据库系统发生错误
     * @throws NoSuchElementException 如果迭代器已经结束
     * @throws TransactionAbortedException 如果事务被中止
     */
    public Tuple next() throws DbException, NoSuchElementException, TransactionAbortedException {
        if (!started)
            throw new DbException("数据库未启动。");

        return op.next();
    }

    /**
     * 关闭查询迭代器，释放资源。
     */
    public void close() {
        op.close(); // 关闭执行计划迭代器
        started = false; // 标记为已关闭
    }

    /**
     * 执行并输出整个查询结果（用于调试或交互式显示）。
     *
     * 会打印表头、每一行数据以及总记录数。
     *
     * @throws DbException 如果数据库系统发生错误
     * @throws TransactionAbortedException 如果事务被中止
     */
    public void execute() throws DbException, TransactionAbortedException {
        TupleDesc td = this.getOutputTupleDesc();

        // 输出表头
        StringBuilder names = new StringBuilder();
        for (int i = 0; i < td.numFields(); i++) {
            names.append(td.getFieldName(i)).append("\t");
        }
        System.out.println(names);

        // 输出分隔线
        for (int i = 0; i < names.length() + td.numFields() * 4; i++) {
            System.out.print("-");
        }
        System.out.println();

        this.start(); // 启动查询

        int cnt = 0;
        while (this.hasNext()) {
            Tuple tup = this.next();
            System.out.println(tup); // 输出每一行数据
            cnt++;
        }

        System.out.println("\n " + cnt + " 行。"); // 输出总行数
        this.close(); // 关闭查询
    }
}
